import _ from 'lodash';
import { IDirectory } from '../../directory';
import {
  XAssignTask,
  XAssignTaskTree,
  XDistribution,
  XReportTaskTree,
  XReportTree,
  XReportTreeNode,
} from '@/ts/base/schema';
import { ReportTaskTree } from '@/ts/core/work/assign/taskTree/ReportTaskTree';
import { ReportTreeBase } from '.';
import { AssignTaskTree } from '@/ts/core/work/assignTree';
import { generateShortUuid } from '@/ts/base/common/uuid';

export class ReportTree extends ReportTreeBase<XReportTree, XReportTreeNode> {
  constructor(_metadata: XReportTree, _directory: IDirectory) {
    super(_metadata, _directory, _directory.resource.reportTreeColl);
  }

  get nodeColl() {
    return this.directory.resource.reportTreeNodeColl;
  }

  async createAssignTree(
    assignTask: XAssignTask,
    destination?: IDirectory,
  ): Promise<XAssignTaskTree | undefined> {
    destination ||= this.directory;
    // 生成新树的元数据，直接设置根节点ID
    const newMetaData: XAssignTaskTree = {
      ...(_.omit(this.metadata, [
        'name',
        'code',
        'createUser',
        'createTime',
        'updateUser',
        'updateTime',
        'rootNodeId',
      ]) as any),
      id: assignTask.treeId,
      modelId: assignTask.modelId,
      period: assignTask.period,
      assignId: assignTask.id,
      code: this.metadata.code + assignTask.period.replace(/-/g, ''),
      name: this.metadata.name + ' ' + assignTask.period,
      rootNodeId: assignTask.id,
    };
    const treeColl = destination.resource.assignTaskTreeColl;
    const data = await treeColl.replace(newMetaData);
    if (!data) return;
    const newTree = new AssignTaskTree(
      data,
      destination,
      treeColl,
      destination.resource.assignTaskPublicColl,
    );
    // 加载所有原始节点并按层级排序
    await this.loadNodes(true);
    const originalRoot = this.nodes.find((n) => n.id === this.metadata.rootNodeId);
    if (!originalRoot) return;
    // BFS
    const sortNodesHierarchically = (nodes: any[], rootId: string) => {
      const sorted = [];
      const queue = [rootId];
      const nodeMap = new Map(nodes.map((n) => [n.id, n]));
      while (queue.length > 0) {
        const node = nodeMap.get(queue.shift()!);
        if (!node) continue;
        sorted.push(node);
        const children = nodes.filter((n) => n.parentId === node.id);
        children.forEach((child) => queue.push(child.id));
      }
      return sorted;
    };
    // 处理节点克隆（不包括根节点）
    const sortedNodes = sortNodesHierarchically(this.nodes, originalRoot.id);
    const idMap = new Map([[originalRoot.id, assignTask.id]]);
    const batchSize = 1000;
    const newNodes = [];
    const insertPromises: Promise<XAssignTask[]>[] = [];
    for (const node of sortedNodes) {
      if (node.id === originalRoot.id) {
        continue;
      }
      const uuid = generateShortUuid();
      idMap.set(node.id, uuid);
      // 更新 nodePath
      const newNodePath = node.nodePath
        .split('/')
        .map((oldId: string) =>
          oldId === originalRoot.id ? assignTask.id : idMap.get(oldId) || oldId,
        )
        .join('/');
      const newNode = {
        ..._.omit(node, ['createTime', 'updateTime']),
        id: uuid,
        treeId: data.id,
        parentId: node.parentId ? idMap.get(node.parentId) || null : null,
        nodePath: newNodePath,
      };
      newNodes.push(newNode);
      if (newNodes.length >= batchSize) {
        insertPromises.push(
          newTree.nodeColl.insertMany(newNodes.splice(0, batchSize) as XAssignTask[]),
        );
      }
    }
    if (newNodes.length > 0) {
      insertPromises.push(newTree.nodeColl.insertMany(newNodes as XAssignTask[]));
    }
    await Promise.all(insertPromises);
    return data;
  }

  async createTaskTree(
    dist: XDistribution,
    destination?: IDirectory,
  ): Promise<XReportTaskTree | undefined> {
    destination ||= this.directory;
    const newMetaData: XReportTaskTree = {
      ...(_.omit(this.metadata, [
        'id',
        'name',
        'code',
        'createUser',
        'createTime',
        'updateUser',
        'updateTime',
      ]) as any),
      taskId: dist.taskId,
      period: dist.period,
      distId: dist.id,
      code: this.metadata.code + dist.period.replace('-', ''),
      name: this.metadata.name + ' ' + dist.period,
    };
    newMetaData.id = 'snowId()';

    const treeColl = destination.resource.genTargetColl<XReportTaskTree>(
      ReportTaskTree.getCollName('报表树'),
    );

    const data = await treeColl.replace(newMetaData);
    if (!data) {
      return;
    }

    console.warn(data);

    // 递归克隆树节点
    await this.loadNodes(true);
    const root = this.nodes.find((n) => n.id == this.metadata.rootNodeId);
    if (!root) {
      return;
    }
    const tree = await this.loadSubTree(root);

    const newTree = new ReportTaskTree(data, destination);
    await newTree.cloneNodes(tree, this.nodes.length, () => {});

    // 设置根节点
    const [newRoot] = await newTree.nodeColl.loadSpace({
      options: {
        match: {
          treeId: newTree.id,
          _or_: [{ parentId: '' }, { parentId: null }, { parentId: { _exists_: false } }],
        },
      },
    });
    console.warn(newRoot);
    newTree.metadata.rootNodeId = newRoot?.id;
    await treeColl.replace(newTree.metadata);

    return newTree.metadata;
  }

  async createTaskTreeDirect(
    dist: XDistribution,
    destination?: IDirectory,
  ): Promise<boolean> {
    destination ||= this.directory;
    const newMetaData: XReportTaskTree = {
      ...(this.metadata as any),
      taskId: dist.taskId,
      period: dist.period,
      distId: dist.id,
      code: this.metadata.code + dist.period.replace('-', ''),
      name: this.metadata.name + ' ' + dist.period,
    };

    const treeColl = destination.resource.genTargetColl<XReportTaskTree>(
      ReportTaskTree.getCollName('报表树'),
    );

    const data = await treeColl.replace(newMetaData);
    if (!data) {
      return false;
    }

    console.warn(data);

    // 递归克隆树节点
    await this.loadNodes(true);

    const newTree = new ReportTaskTree(data, destination);
    await newTree.nodeColl.insertMany(this.nodes);

    return true;
  }
}
